package org.zoomdev.zoom.ioc;

import org.zoomdev.zoom.common.exceptions.ZoomException;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;

public final class IocUtils {


    public static ParameterizedType createGenericType(Class<?> rawClass, Type[] elementClasses) {
        return new GenericType(rawClass, elementClasses);
    }

    public static Type createParameterizedType(ParameterizedType type) {
        Type rawType = type.getRawType();
        assert (rawType instanceof Class);
        Type[] realTypes = type.getActualTypeArguments();
        if (realTypes == null || realTypes.length == 0) {
            return rawType;
        }
        Type[] created = new Type[realTypes.length];
        for (int i = 0; i < realTypes.length; ++i) {
            created[i] = createGenericType(realTypes[i]);
        }
        return createGenericType((Class<?>) rawType, created);
    }

    /**
     * 包含泛型结果
     * <pre>
     *    比如 <T> T someMethod();
     *        <T> List<T> someMethod();
     * </pre>
     *
     * @return
     */
    public static boolean containTypeParameters(Type type) {
        if (type instanceof IocUtils.GenericType) {
            return false;
        }

        if (type instanceof Class) {
            return false;
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) type;
            if (containTypeParameters(p.getRawType())) {
                return true;
            }
            if (p.getActualTypeArguments() != null) {
                for (Type tt : p.getActualTypeArguments()) {
                    if (containTypeParameters(tt)) {
                        return true;
                    }
                }
            }

        }

        return type instanceof TypeVariable;
    }

    /**
     * 获取一个方法的标准返回(本框架),
     * 对于一个泛型返回，转化为IocUtils.GenericType
     *
     * @param method
     * @return
     */
    public static Type getStandardReturnType(Method method) {
        Type returnType = method.getGenericReturnType();
        if (!containTypeParameters(returnType)) {
            returnType = createGenericType(returnType);
        } else {
            //降一个级别
            returnType = method.getReturnType();
        }
        return returnType;
    }

    public static Type createGenericType(Type type) {

        if (type instanceof IocUtils.GenericType) {
            return type;
        }

        if (type instanceof Class) {
            return type;
        }

        if (type instanceof ParameterizedType) {
            return createParameterizedType((ParameterizedType) type);
        }


        throw new ZoomException("不支持的ioc类型" + type);
    }

    public static boolean isGenericType(IocKey key) {
        return key != null && key.getType() instanceof GenericType;
    }

    public static boolean isGenericType(Type type) {
        return type instanceof GenericType;
    }

    //getGenericReturnType
    public static class GenericType implements ParameterizedType {

        private final Class<?> rawClass;
        private final Type[] elementClasses;
        int h;

        public GenericType(Class<?> rawClass, Type[] elementClasses) {
            this.rawClass = rawClass;
            this.elementClasses = elementClasses;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(rawClass.getName());
            if (this.elementClasses != null && this.elementClasses.length > 0) {
                sb.append("<");
                for (int i = 0; i < elementClasses.length; ++i) {
                    Type type = this.elementClasses[i];
                    sb.append(toString(type));
                    sb.append(",");
                }
                sb.setLength(sb.length() - 1);
                sb.append(">");
            }
            return sb.toString();
        }

        @Override
        public Type[] getActualTypeArguments() {
            return elementClasses;
        }

        @Override
        public Type getRawType() {
            return rawClass;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof GenericType) {
                GenericType o = (GenericType) obj;
                if (rawClass != o.rawClass) {
                    return false;
                }

                return Arrays.equals(elementClasses, o.elementClasses);
            }
            return false;
        }

        @Override
        public int hashCode() {
            int h = this.h;
            if (h == 0) {
                h = 31 * h + rawClass.hashCode();
                for (Type t : elementClasses) {
                    h = 31 * h + t.hashCode();
                }
                this.h = h;
            }
            return h;
        }

        @Override
        public Type getOwnerType() {
            return null;
        }

        protected String toString(Type type) {
            if (type instanceof Class) {
                return ((Class<?>) type).getName();
            }
            return type.toString();
        }
    }

}
